/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2021年6月24日
 */
package com.massyframework.beanskin.sample.graphql;

import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;

import javax.annotation.Resource;
import javax.script.ScriptEngine;
import javax.script.ScriptException;

import org.springframework.stereotype.Component;

import com.massyframework.beanskin.assembly.Assembly;
import com.massyframework.beanskin.graphql.context.RuntimeWiringFactory;
import com.massyframework.beanskin.graphql.util.GraphQLUtils;
import com.massyframework.beanskin.graphql.util.expression.ExpressionPredicate;
import com.massyframework.beanskin.graphql.util.pagelist.PageList;
import com.massyframework.beanskin.graphql.util.pagelist.PageRequest;
import com.massyframework.beanskin.sample.ToDoItem;
import com.massyframework.beanskin.sample.ToDoItemRepository;

import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.idl.RuntimeWiring;

/**
 * 缺省的运行植入工厂
 * @author huangkh
 *
 */
@Component("runtimeWireFactory")
public class DefaultRuntimeWireFactory implements RuntimeWiringFactory {
	
	private ToDoItemRepository toDoItemRepository;
	private ScriptEngine scriptEngine;

	/**
	 * 
	 */
	public DefaultRuntimeWireFactory() {
	}

	@Override
	public RuntimeWiring createRuntimeWiring(Assembly assembly) {
		ItemsDataFetcher itemDataFetcher = new ItemsDataFetcher();
		return RuntimeWiring.newRuntimeWiring()
				.type("Query", typeWiring-> typeWiring
					.dataFetcher("findById", new FindByIdDataFetcher())
					.dataFetcher("getItems", itemDataFetcher)
					.dataFetcher("getItemsWithExpression", itemDataFetcher)
					.dataFetcher("getItemsWithPage", new PageDataFetcher())
				)
				.build();
	}

	/**
	 * 待办事项资源库
	 * @return {@link ToDoItemRepository}
	 */
	public ToDoItemRepository getToDoItemRepository() {
		return toDoItemRepository;
	}

	/**
	 * 设置待办事项资源库
	 * @param toDoItemRepository {@link ToDoItemRepository}
	 */
	@Resource
	public void setToDoItemRepository(ToDoItemRepository toDoItemRepository) {
		this.toDoItemRepository = toDoItemRepository;
	}
	
	/**
	 * 脚本引擎
	 * @return {@link ScriptEngine}
	 */
	public ScriptEngine getScriptEngine() {
		return this.scriptEngine;
	}

	/**
	 * 脚本引擎
	 * @param value {@link ScriptEngine}
	 */
	@Resource
	public void setScriptEngine(ScriptEngine value) {
		this.scriptEngine = value;
	}


	private class FindByIdDataFetcher implements DataFetcher<ToDoItem> {

		@Override
		public ToDoItem get(DataFetchingEnvironment env) throws Exception {
			Integer id = env.getArgument("id");
			

			return getToDoItemRepository().findById(id);
		}
	}
	
	private class ItemsDataFetcher implements DataFetcher<List<ToDoItem>> {

		@Override
		public List<ToDoItem> get(DataFetchingEnvironment env) throws Exception {
			if (GraphQLUtils.fieldNameEquals(env.getFieldDefinition(), "getItemsWithExpression")) {
				return  this.getItems(env);
			}
			
			return this.getItems();
		}	
		
		/**
		 * 获取所有待办事项
		 * @return {@link List}
		 */
		protected List<ToDoItem> getItems(){
			return getToDoItemRepository().getToDoItems();
		}
		
		/**
		 * 按表达式获取所有待办事项
		 * @param env {@link DataFetchingEnvironment}
		 * @return {@link List}
		 */
		protected List<ToDoItem> getItems(DataFetchingEnvironment env) throws ScriptException{
			ExpressionPredicate<ToDoItem> filter =
					ExpressionPredicate.newInstance(env, "exp", getScriptEngine(), ToDoItem.class);
			return getToDoItemRepository().getToDoItems(filter);
		}
	}
	
	
	
	private class PageDataFetcher implements DataFetcher<PageList<ToDoItem>> {

		@Override
		public PageList<ToDoItem> get(DataFetchingEnvironment env) throws Exception {
			ExpressionPredicate<ToDoItem> filter =
					ExpressionPredicate.newInstance(env, "exp", getScriptEngine(), ToDoItem.class);
			
			PageRequest request = 
					PageRequest.newBuilder(env, "page")
						.build();
						
			//分页
			int count = getToDoItemRepository().size();
			Function<Stream<ToDoItem>, Stream<ToDoItem>> action = stream -> {
				return stream.filter(filter)
					.limit(request.getSize())
					.skip(request.getSize() * request.getIndex());
			};
			List<ToDoItem> items = getToDoItemRepository().getToDoItems(action);
						
			//填充返回结果
			PageList<ToDoItem> result = new PageList<ToDoItem>();
			result.setCount(count);
			result.setIndex(request.getIndex());
			result.setItems(items);
			return result;
		}
		
	}
}
